﻿#region "Copyright (C) Lenny Granovsky. 2005-2013"
//This program is free software distribute under the terms of the GNU General Public License as published by
//the Free Software Foundation. Please read AssemblyInfo.cs file for more information.
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Web.Enhancements.Rest
{
    internal class TypeMapper
    {
        public TypeMapper()
        {
        }

        public MappingResult MapRequest(string appRelativeFilePath, string queryString, string httpMethod)
        {
            //extract class name
            string className = appRelativeFilePath;
            if (className.StartsWith("~/"))
                className = className.Remove(0, 2);
            className = className.Substring(0, className.LastIndexOf('.')).Replace('/', '.');

            //extract method name
            string method = "";
            bool isDefault = false;
            int pos1 = queryString.IndexOf("&");
            if (pos1 < 0)
                method = queryString;
            else
                method = queryString.Substring(0, pos1);
            if (method.StartsWith("method=", StringComparison.OrdinalIgnoreCase))
                method = method.Remove(0, 7);//remove QueryString var name if present
            if (method.Length == 0 || method.IndexOf("=") > -1)
            {
                method = httpMethod.ToUpper();
                isDefault = true;
            }

            string assemblyName = "";
            className = TypeMapper.TranslateToType(className, out assemblyName);

            //return class name and assembly name via output parameter if found.
            return new MappingResult()
                {
                    AssemblyName = assemblyName,
                    ClassTypeName = className,
                    MethodName = method,
                    IsDefault = isDefault
                };
        }

        /// <summary>
        /// Method inserts new mappings from known type. This method is designed to be used from WebRestHandlerFactory when it 
        /// determins type dinamically to boost performance next time the same request comes.
        /// </summary>
        /// <param name="fullName"></param>
        /// <param name="assemblyName"></param>
        public static void InsertDeterminedAliasMapping(string fullName, string assemblyName)
        {
            if (fullName.Length == 0 || assemblyName.Length == 0)
                throw new ArgumentException("Full type name and assembly name must be provided for InsertDeterminedAliasMapping method.");

            WebRestClassConfiguration classConfig = new WebRestClassConfiguration()
            {
                AliasName = fullName,
                AssemblyName = assemblyName,
                FullName = fullName,
                FullQualifiedName = string.Format("{0}, {1}", fullName, assemblyName)
            };
            lock (WebRestSettings.Current.TypeMappings)
            {
                if (!WebRestSettings.Current.TypeMappings.ContainsKey(fullName))
                    WebRestSettings.Current.TypeMappings.Add(fullName, classConfig);
            }
        }

        /// <summary>
        /// Method returns full type name and assembly name from full qualified type name.
        /// </summary>
        /// <param name="fullQualifiedTypeName">Full qualified type name, including namespace and assembly name.</param>
        /// <param name="assemblyName">Returns an assembly name.</param>
        /// <returns>Returns full type name, including namespace, but not assembly name.</returns>
        public static string ExtractFullTypeName(string fullQualifiedTypeName, out string assemblyName)
        {
            assemblyName = "";
            string fullName = "";
            int pos = fullQualifiedTypeName.IndexOf(",");
            if (pos > 0)
            {
                assemblyName = fullQualifiedTypeName.Substring(pos + 1).Trim();
                fullName = fullQualifiedTypeName.Substring(0, pos).Trim();
            }
            else fullName = fullQualifiedTypeName;
            return fullName;
        }

        /// <summary>
        /// Method converts Class (type) alias to a real type FullName and its assembly name. 
        /// If FullName of the type was supplied, then it returns this FullName and assembly.
        /// </summary>
        /// <param name="alias"></param>
        /// <param name="assemblyName"></param>
        /// <returns></returns>
        public static string TranslateToType(string alias, out string assemblyName)
        {
            string aName = "";
            string typeName = MapTypeFromAlias(alias, out aName);
            assemblyName = aName.Length > 0 ? aName : "";
            if (typeName.Length > 0)
                return typeName;
            return alias;
        }

        private static string MapTypeFromAlias(string alias, out string assemblyName)
        {
            string typeName = "";
            assemblyName = "";
            WebRestClassConfiguration conf = WebRestSettings.Current.TypeMappings.Find(alias);
            if (conf != null)
            {
                typeName = conf.FullName;
                assemblyName = conf.AssemblyName;
            }
            return typeName;
        }

        /// <summary>
        /// Maps full class type name to its alias
        /// </summary>
        /// <param name="typeFullName">Full type name including namespace</param>
        /// <returns></returns>
        public static string TranslateFromType(string classFullName, string assemblyName)
        {
            if (WebRestSettings.Current.TypeMappings.Contains(classFullName, assemblyName))
                return WebRestSettings.Current.TypeMappings.Find(classFullName, assemblyName).AliasName;

            return classFullName;
        }
    }

    internal class MappingResult
    {
        public string ClassTypeName { get; set; }
        public string AssemblyName { get; set; }
        public string MethodName { get; set; }
        public bool IsDefault { get; set; }

        public MappingResult()
        {
            this.AssemblyName = "";
            this.ClassTypeName = "";
            this.MethodName = "";
        }
    }
}
